
#include <time.h>
#include <BFont>
#include <BKeyEvent>
#include <BCharEvent>
#include <BMouseEvent>
#include <BChangeEvent>
#include <BFocusEvent>
#include <BPainter>

#include <BSpinBox>
#include "member_BSpinBox.h"

using namespace BWE;

#define member					(*(member_BSpinBox*)_ptr)
#define member_allocate()		_ptr = new member_BSpinBox(this)
#define member_release()		delete (member_BSpinBox*)_ptr

BSpinBox::BSpinBox(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BSpinBox::BSpinBox(int minimum, int maximum, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setRange(minimum, maximum);
}
BSpinBox::~BSpinBox()
{
	member_release();
}

void BSpinBox::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		this->fresh();
	}
}
Align BSpinBox::align() const
{
	return member.align;
}

void BSpinBox::setValue(int value)
{
	value = member.range.limit(value);
	if(member.value != value)
	{
		member.value = value;
		member.text = value;
		this->fresh();
		emit(Signal_ValueChanged, member.value);
	}
}
int BSpinBox::value() const
{
	return member.value;
}

void BSpinBox::setRange(int minimum, int maximum)
{
	setRange(BRange(minimum, maximum));
}
void BSpinBox::setRange(const BRange& range)
{
	if (member.range != range)
	{
		member.range = range;
		int value = member.range.limit(member.value);
		this->setValue(value);
		emit(Signal_RangeChanged, member.range);
	}
}
const BRange& BSpinBox::range() const
{
	return member.range;
}

void BSpinBox::setMinimum(int minimum)
{
	return setRange(minimum, member.range.max());
}
int BSpinBox::minimum() const
{
	return member.range.min();
}

void BSpinBox::setMaximum(int maximum)
{
	return setRange(member.range.min(), maximum);
}
int BSpinBox::maximum() const
{
	return member.range.max();
}

void BSpinBox::setPrefix(const BString& prefix)
{
	if(member.prefix != prefix)
	{
		member.prefix = prefix;
		this->fresh();
	}
}
const BString& BSpinBox::prefix() const
{
	return member.prefix;
}

void BSpinBox::setSuffix(const BString& suffix)
{
	if(member.suffix != suffix)
	{
		member.suffix = suffix;
		this->fresh();
	}
}
const BString& BSpinBox::suffix() const
{
	return member.suffix;
}

void BSpinBox::setStep(int step)
{
	member.step = step;
}
int BSpinBox::step() const
{
	return member.step;
}

BSize BSpinBox::sizeHint() const
{
	BSize hintSize = this->shellSize();
	const BFont* font = this->font();
	if(font)
	{
		hintSize += font->size();
	}
	return hintSize;
}

void BSpinBox::changeEvent(const BChangeEvent& changeEvent)
{
	if (changeEvent.change() == Change_Style)
	{
		//hintSize();
	}
}
void BSpinBox::updateEvent(const BEvent& event)
{
	if (this->focus() && member.inputCount == 0)
	{
		static clock_t tick = 0;
		clock_t now = clock();
		if (now - tick > 500)
		{
			tick = now;
			member.inputShown = !member.inputShown;
			this->fresh();
		}
		if (tick > now)
			tick = 0;
	}
}
void BSpinBox::freshEvent(const BEvent& event)
{
	member.freshRects();
}
void BSpinBox::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawClient();

	if (this->focus() && member.inputCount > 0)
	{
		int left = member.rects[member.inputIndex].x();
		int right = member.rects[member.inputIndex + member.inputCount].x();
		painter.setColor(Color_Checked);
		painter.fillRect(left, member.textRect.top(), right - left, member.textRect.height());
	}

	painter.setColor(Color_Text);
	painter.drawText(member.textRect, member.text);

	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	if (member.inputShown && member.inputCount == 0)
	{
		const BRect& rect = member.rects[member.inputIndex];
		int x = rect.left();
		int y = rect.center().y();
		int hh = member.textRect.height() * 0.6;
		if (painter.setColor(Color_Indicator))
		{
			painter.drawLine(x, y - hh, x, y + hh);
			painter.drawLine(x - 1, y - hh, x + 2, y - hh);
			painter.drawLine(x - 1, y + hh, x + 2, y + hh);
		}
	}

	int hoverIndex = member.hoverIndex;
	if (0)//hoverIndex >= 0)
	{
		const BRect& rect = member.rects[member.hoverIndex];
		int x = rect.left();
		int y = rect.center().y();
		int hh = member.textRect.height() * 0.6;

		painter.setLineWidth(1);
		painter.setColor(0, 255, 100);
		painter.drawLine(x, y - hh, x, y + hh);
	}
}

void BSpinBox::focusEvent(const BFocusEvent& focusEvent)
{
	if (focusEvent.focus() == Focus_In)
	{
		member.justFocused = true;
		member.inputIndex = 0;
		member.inputCount = member.text.count();
		this->fresh();
	}
	if (focusEvent.focus() == Focus_Out)
	{
		if (member.changed)
		{
			int value = member.text.toInt();
			this->setValue(value);
			emit(Signal_EditFinished, member.value);
			member.changed = false;
		}
		this->fresh();
	}
}
void BSpinBox::keyDownEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	switch (key)
	{
	case Key_Left:
	{
		if (member.inputCount > 0)
		{
			member.inputCount = 0;
			this->fresh();
		}
		else if (member.inputIndex > 0)
		{
			member.inputIndex--;
			this->fresh();
		}
		break;
	}
	case Key_Right:
	{
		if (member.inputCount > 0)
		{
			member.inputIndex += member.inputCount;
			member.inputCount = 0;
			this->fresh();
		}
		else if (member.inputIndex < member.text.size())
		{
			member.inputIndex++;
			this->fresh();
		}
		break;
	}
	case Key_Back:
	{
		if (member.inputCount > 0)
		{
			if (member.text.remove(member.inputIndex, member.inputCount))
			{
				member.inputCount = 0;
				member.changed = true;
				this->fresh();
				int value = member.text.toInt();
				setValue(value);
				emit(Signal_Edited, member.value);
			}
		}
		else
		{
			member.inputIndex--;
			if (member.text.remove(member.inputIndex))
			{
				member.changed = true;
				this->fresh();
				int value = member.text.toInt();
				setValue(value);
				emit(Signal_Edited, member.value);
			}
		}
		break;
	}
	case Key_Delete:
	{
		if (member.inputCount > 0)
		{
			if (member.text.remove(member.inputIndex, member.inputCount))
			{
				member.inputCount = 0;
				member.changed = true;
				this->fresh();
				int value = member.text.toInt();
				setValue(value);
				emit(Signal_Edited, member.value);
			}
		}
		else
		{
			if (member.text.remove(member.inputIndex))
			{
				member.changed = true;
				this->fresh();
				int value = member.text.toInt();
				setValue(value);
				emit(Signal_Edited, member.value);
			}
		}
		break;
	}
	case Key_Enter:
	{
		this->setFocus(false);
		break;
	}
	}
}
void BSpinBox::keyUpEvent(const BKeyEvent& keyEvent)
{
	this->fresh();
}
void BSpinBox::charEvent(const BCharEvent& charEvent)
{
	BCode code = charEvent.code();
	if (code == '-' && member.inputIndex != 0)
		return;
	if (48 <= code && code < 58 || code == '-')
	{
		if (member.inputCount > 0)
		{
			member.text.remove(member.inputIndex, member.inputCount);
			member.inputCount = 0;
		}
		if (member.text.insert(member.inputIndex, code))
		{
			member.inputIndex++;
			member.changed = true;
			this->fresh();
			int value = member.text.toInt();
			setValue(value);
			emit(Signal_Edited, member.value);
			this->fresh();
		}
	}
}

void BSpinBox::mouseEnterEvent(const BMouseEvent& event)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
}
void BSpinBox::mouseLeaveEvent(const BMouseEvent& event)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
}

void BSpinBox::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (member.justFocused)
	{
		member.justFocused = false;
		this->fresh();
		return;
	}
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		int hoverIndex = member.hoverIndex;
		if (member.inputCount > 0 || member.inputIndex != member.hoverIndex)
		{
			member.inputIndex = member.hoverIndex;
			member.inputCount = 0;
			this->fresh();
		}
		member.pressed = true;
		member.pressedIndex = member.hoverIndex;
	}
}
void BSpinBox::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	member.pressed = false;
	this->fresh();
}
void BSpinBox::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	int index = 0;
	while (index < member.rects.size())
	{
		const BRect& rect = member.rects[index];
		if (rect.center().x() > pos.x())
			break;
		index++;
	}
	if (member.hoverIndex != index)
	{
		member.hoverIndex = index;
		this->fresh();
	}
	if (member.pressed)
	{
		member.inputIndex = bMin(member.pressedIndex, member.hoverIndex);
		member.inputCount = abs(member.pressedIndex - member.hoverIndex);
		this->fresh();
	}
}
void BSpinBox::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.wheel() > 0)
	{
		this->setValue(member.value + member.step);
		emit(Signal_Edited, member.value);
	}
	else
	{
		this->setValue(member.value - member.step);
		emit(Signal_Edited, member.value);
	}
}
void BSpinBox::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	member.inputIndex = 0;
	member.inputCount = member.text.count();
	this->fresh();
}

